package org.lex.lang;

public class StackTrace {
	public static void main(String[] args) {
		test();
	}

	static void test() {
		StackTrace t = StackTrace.currentInstance();
		while (t.hasPreviousTrace()) {
			System.out.println("Class : " + t.getCallerClass());
			System.out.println("Method: " + t.getCallerMethodName());
			System.out.println("CL    : " + t.getCallerLoader());
			System.out.println("File  : " + t.getCallerFile());
			System.out.println("Line  : " + t.getCallerLineNumber());
			System.out.println("---------------------------------");
			t = t.getPreviousTrace();
		}
	}

	public static StackTrace currentInstance() {
		return new StackTrace();
	}

	// getter
	public StackTraceElement getTraceElement() {
		return traces[currentTraceIndex];
	}

	public Class<?> getCallerClass() {
		try {
			return Class.forName(this.getTraceElement().getClassName());
		} catch (ClassNotFoundException cannotHappen) {
			cannotHappen.printStackTrace();
			return null;
		}
	}

	public ClassLoader getCallerLoader() {
		return this.getCallerClass().getClassLoader();
	}

	public String getCallerMethodName() {
		return this.getTraceElement().getMethodName();
	}

	public String getCallerFile() {
		return this.getTraceElement().getFileName();
	}

	public int getCallerLineNumber() {
		return this.getTraceElement().getLineNumber();
	}

	// trace chains
	public boolean hasPreviousTrace() {
		return currentTraceIndex < traces.length;
	}

	public StackTrace getPreviousTrace() {
		if (this.hasPreviousTrace()) {
			return new StackTrace(this.traces, this.currentTraceIndex + 1);
		} else {
			return null;
		}
	}

	public boolean hasNextTrace() {
		return currentTraceIndex > 2;
	}

	public StackTrace getNextTrace() {
		if (this.hasNextTrace()) {
			return new StackTrace(this.traces, this.currentTraceIndex - 1);
		} else {
			return null;
		}
	}

	private final StackTraceElement[] traces;
	private final int currentTraceIndex;

	private StackTrace() {
		try {
			throw new Exception();
		} catch (Exception e) {
			traces = e.getStackTrace();
			currentTraceIndex = 2;
		}
	}

	private StackTrace(StackTraceElement[] trace, int currentTraceIndex) {
		this.traces = trace;
		this.currentTraceIndex = currentTraceIndex;
	}
}
